#include "redaudio.h"

dMusic::dMusic (Mix_Music *music)
{
  type = 'z';
  typeStr = "MUSIC";
  this->music = music;
}

dMusic::~dMusic ()
{
  Mix_FreeMusic (music);
  music=NULL;
}

string
dMusic::toString ()
{
  return typeStr;
}

dMixChunk::dMixChunk (Mix_Chunk *sample)
{
  type = 'z';
  typeStr = "MIXCHUNK";
  this->sample = sample;

}

dMixChunk::~dMixChunk ()
{
  Mix_FreeChunk (sample);
  sample=NULL;
}

void
dMixChunk::addChannel (int c)
{
  channels->append (new dNumber (c));
}

string
dMixChunk::toString ()
{
  return typeStr;
}

extern "C" void
load_module ()
{
  addToSystem ("init-audio", dInitAudio);
  addToSystem ("make-music", dMakeMusic);
  addToSystem ("play-music", dPlayMusic);
  addToSystem ("pause-music", dPauseMusic);
  addToSystem ("stop-music", dStopMusic);
  addToSystem ("set-music-volume", dSetMusicVolume);
  addToSystem ("set-channels", dMix_allocatechannels);
  addToSystem ("make-sample", dMix_loadwav);
  addToSystem ("play-sample", dMix_fadeinchannel);
  addToSystem ("pause-sample", dMix_togglepause);
  //atexit (Mix_CloseAudio);
  //atexit (SDL_Quit);
}

dObject *
dInitAudio (dList *args)
{
  if (SDL_Init (SDL_INIT_AUDIO) == -1)
    return new dError ("failed to initialize SDL_AUDIO");
  int rate = 22050;
  int channels = 2;
  int chunksize = 2048;
  if (args != 0)
    {
      dNumber *temp;
      if ((*args)[0] != nil)
	{
	  if ((*args)[0]->type == 'i')
	    rate = toInt ((dNumber*)(*args)[0]);
	  else
	    return new dError ("init-audio accepts 3 integer arguments");
	}
      if ((*args)[1] != nil)
	{
	  if ((*args)[1]->type == 'i')
	    channels = toInt ((dNumber*)(*args)[1]);
	  else
	    return new dError ("init-audio accepts 3 integer arguments");
	}
      if ((*args)[2] != nil)
	{
	  if ((*args)[2]->type == 'i')
	    chunksize = toInt ((dNumber*)(*args)[2]);
	  else
	    return new dError ("init-audio accepts 3 integer arguments");
	}
    }
  if (Mix_OpenAudio (rate, MIX_DEFAULT_FORMAT, channels, chunksize) == -1)
    {
      printf ("init-audio::Mix_OpenAudio: %s:\n", Mix_GetError ());
      return new dError ("init-audio failed to initialize SDL_mixer");
    }
  addToSystem ("*music-volume*", new dNumber (Mix_VolumeMusic (-1)));
  return T;
}

/********************************
 * Calls int Mix_AllocateChannels (int numchans)
 ********************************/
dObject *
dMix_allocatechannels (dList *args)
{
  if (args == 0)
    return new dError("set-channels: requires 1 argument");

  int numchans;
  if ((*args)[0]->type != 'i')
    return new dError ("set-channels: argument 1 must be an integer");
  numchans = toInt ((dNumber*)(*args)[0]);
  return new dNumber (Mix_AllocateChannels (numchans));
}


/******************************
 * Mix_Music-Related Functions
 ******************************/

dObject *
dMakeMusic (dList *args)
{
  if (args == 0 || (*args)[0]->type != 's')
    return new dError ("make-music accepts a single string argument");
  if (!FileExists ((*args)[0]->toString ()))
    return new dError ("file does not exist");
  dMusic *temp = new dMusic (Mix_LoadMUS ((*args)[0]->toString ().c_str ()));
  if (!temp->music)
    {
      printf ("make-music::Mix_LoadMUS: %s\n", Mix_GetError ());
      return new dError ("make-music could not load file");
    }
  return temp;
}

dObject *
dPlayMusic (dList *args)
{
  
  if (args == 0)
    return new dError ("play accepts a MUSIC object or a filename, perhaps followed by an integer");
  dMusic *temp;
  int milliseconds = 0;
  debug (args);
  if ((*args)[0]->typeStr == "MUSIC")
    temp = (dMusic*)(*args)[0];
  else if ((*args)[0]->type == 's')
    {
      temp = new dMusic (Mix_LoadMUS ((*args)[0]->toString ().c_str ()));
      if (!temp->music)
	{
	  printf ("play-music::Mix_LoadMUS: %s\n", Mix_GetError ());
	  return new dError ("play-music could not load file");
	}
    }
  else
    return new dError ("play accepts a MUSIC object or a filename");
  if ((*args)[1] != nil)
    {
      if ((*args)[1]->type != 'i')
	return new dError ("play-music's second parameter is an integer");
      else
	milliseconds = 1000 * toInt ((dNumber*)(*args)[1]);
    }
  if (Mix_FadeInMusic (temp->music, 0, milliseconds) == -1)
    {
      printf ("play::Mix_FadeInMusic: %s\n", Mix_GetError ());
      return new dError ("play could not play file");
    }
  addToSystem ("*music-playing*", T);
  addToSystem ("*music-paused*", nil);
  return T;
}

dObject *
dPauseMusic (dList *args)
{
  if (Mix_PlayingMusic () == 0)
    return nil;
  if (Mix_PausedMusic () == 0)
    {
      Mix_PauseMusic ();
      addToSystem ("*music-paused*", T);
    }
  else
    {
      Mix_ResumeMusic ();
      addToSystem ("*music-paused*", nil);
    }
  return T;
}

dObject *
dStopMusic (dList *args)
{
  int milliseconds = 0;
  if (args != 0)
    {
      if ((*args)[0]->type != 'i')
	return new dError ("stop-music accepts zero or one integer argument");
      else
	milliseconds = 1000 * toInt ((dNumber*)(*args)[0]);
    }
  if (Mix_PlayingMusic () == 0)
    return nil;
  Mix_FadeOutMusic (milliseconds);
  addToSystem ("*music-playing*", nil);
  addToSystem ("*music-paused*", nil);
  return T;
}

dObject *
dSetMusicVolume (dList *args)
{
  int volume = MIX_MAX_VOLUME;
  if (args != 0)
    {
      if ((*args)[0]->type != 'i')
	return new dError ("set-music-volume accepts 0 or 1 integer argument whose range is 0-100");
      else
	volume = toInt ((dNumber*)(*args)[0]);
    }
  Mix_VolumeMusic (volume);
  dNumber *temp = new dNumber (Mix_VolumeMusic (-1));
  addToSystem ("*music-volume*", temp);
  return temp;
}


/******************************
 * Mix_Chunk-Related Functions
 ******************************/

/********************************
 * Calls Mix_Chunk* Mix_LoadWAV (char* file)
 ********************************/
dObject *
dMix_loadwav (dList *args)
{
  if (args == 0)
    return new dError("make-sample: requires 1 argument");

  if ((*args)[0]->type != 's')
    return new dError ("make-sample: argument 1 must be a string");
  return new dMixChunk (Mix_LoadWAV (((dString*)(*args)[0])->value.c_str ()));
}

/********************************
 * Calls int Mix_FadeInChannel (int channel, Mix_Chunk* chunk, int loops, int ms)
 ********************************/
dObject *
dMix_fadeinchannel (dList *args)
{
  if (args == 0)
    return new dError ("play-sample: requires  4 arguments");

  dMixChunk *chunk;
  if ((*args)[0]->typeStr != "MIXCHUNK")
    return new dError ("play-sample: argument 1 must be an audio sample");
  chunk = (dMixChunk*)(*args)[0];
  int ms = 0;
  if ((*args)[1] != nil)
    {
      if ((*args)[1]->type != 'i')
	return new dError ("play-sample: argument 2 must be an integer");
      else
	ms = toInt ((dNumber*)(*args)[1]) * 1000;
    }
  int loops = 0;
  if ((*args)[2] != nil)
    {
      if ((*args)[2]->type != 'i')
	return new dError ("play-sample: argument 3 must be an integer");
      else
	loops = toInt ((dNumber*)(*args)[2]);
    }
  chunk->addChannel (Mix_FadeInChannel (-1, chunk->sample, loops, ms));
  return (dNumber*)(*chunk->channels)[chunk->channels->size-1];
}

dObject *
dMix_togglepause (dList *args)
{
  int channel;
  if (args == 0)
    channel = -1;
  if ((*args)[0]->typeStr == "MIXCHUNK")
    {
      dMixChunk *temp = (dMixChunk*)(*args)[0];
      
      channel = toInt ((dNumber*)(*temp->channels)[temp->channels->size-1]);
    }
  else if ((*args)[0]->type == 'i')
    channel = toInt ((dNumber*)(*args)[0]);
  else
    return new dError ("pause-sample: requires 1 argument, integer or audio sample");
  if (Mix_Playing (channel) == 1)
    {
      Mix_Pause (channel);
      return T;
    }
  Mix_Resume (channel);
  return nil;
}
